#include "DoorModule.h"

#define DOOR_PTR_TABLE	0x100200

using namespace std;

char _buf[100];

Door::Door(uint x2, uint y2, uint type2, uint ptr, Rom& rom) {
  x = x2;
  y = y2;
  type = type2;

  if ((type == 1) || (type == 3) || (type == 4)) { // Rope+Ladder/Escalator/Stairs
    misc = ptr;
  } else {
    uint addr = ptr + 0xf0200;
    if (type == 2) { // Door
      pointer = rom.readMulti(addr, 4);
      flag = rom.readMulti(addr+4, 2);
      destY = rom.read(addr+6) & ((rom.read(addr+7) & 0x3f) << 8);
      destX = rom.readMulti(addr+8, 2);
      style = rom.read(addr+10);
      direction = rom.read(addr+7) >> 6;
    } else if (type == 0) { // Switch
      flag = rom.readMulti(addr, 2);
      pointer = rom.readMulti(addr+2, 4);
    } else if ((type == 5) || (type == 6)) { // Object/Person
      pointer = rom.readMulti(addr, 4);
    }
  }
}

Door::Door(TiXmlElement* entry, map<string, int> &labels) {
  TiXmlHandle hEntry = TiXmlHandle(entry);
  x = atoi_prefix(hEntry.FirstChild("X").Element()->GetText());
  y = atoi_prefix(hEntry.FirstChild("X").Element()->GetText());
  setTypeFromString(hEntry.FirstChild("Type").Element()->GetText());

  if ((type == 3) || (type == 4)) {
    setStairsDirection(atoi_prefix(hEntry.FirstChild("StairsDirection").Element()->GetText()));
  } else if (type == 2) { // Door
    destX = atoi_prefix(hEntry.FirstChild("WarpX").Element()->GetText());
    destY = atoi_prefix(hEntry.FirstChild("WarpY").Element()->GetText());
    direction = atoi_prefix(hEntry.FirstChild("WarpDirection").Element()->GetText());
    style = atoi_prefix(hEntry.FirstChild("Style").Element()->GetText());
    pointer = userStrToSnesAddr(hEntry.FirstChild("Pointer").Element()->GetText(), labels);
    flag = atoi_prefix(hEntry.FirstChild("Flag").Element()->GetText());
  } else if (type == 0) {
    pointer = userStrToSnesAddr(hEntry.FirstChild("Pointer").Element()->GetText(), labels);
    flag = atoi_prefix(hEntry.FirstChild("Flag").Element()->GetText());
  } else if ((type == 5) || (type == 6)) {
    pointer = userStrToSnesAddr(hEntry.FirstChild("Pointer").Element()->GetText(), labels);
  }
}

void Door::setTypeFromString(const char* str) {
  if (strcmp(str, "Switch") == 0)
    type = 0;
  else if (strcmp(str, "Rope") == 0) {
    type = 1;
    misc = 0x8000;
  } else if (strcmp(str, "Ladder") == 0) {
    type = 1;
    misc = 0;
  } else if (strcmp(str, "Door") == 0)
    type = 2;
  else if (strcmp(str, "Escalator") == 0)
    type = 3;
  else if (strcmp(str, "Stairway") == 0)
    type = 4;
  else if (strcmp(str, "Object") == 0)
    type = 5;
  else if (strcmp(str, "Person") == 0)
    type = 6;
  else { // Invalid input, just set to Ladder
    type = 1;
    misc = 0;
  }
}

TiXmlElement* Door::createXmlEntry() {
  TiXmlElement *entry = new TiXmlElement("Door"), *field;

  field = new TiXmlElement("X");
  itoa(x, _buf, 10);
  field->LinkEndChild(new TiXmlText(_buf));
  entry->LinkEndChild(field);

  field = new TiXmlElement("Y");
  itoa(y, _buf, 10);
  field->LinkEndChild(new TiXmlText(_buf));
  entry->LinkEndChild(field);

  field = new TiXmlElement("Type");
  if (type == 0) {
    field->LinkEndChild(new TiXmlText("Switch"));
    entry->LinkEndChild(field);

    field = new TiXmlElement("Flag");
    itoa_prefix(flag, _buf, 16);
    field->LinkEndChild(new TiXmlText(_buf));
    entry->LinkEndChild(field);

    field = new TiXmlElement("Pointer");
    _buf[0] = '$';
    itoa(pointer, &(_buf[1]), 16);
    field->LinkEndChild(new TiXmlText(_buf));
    entry->LinkEndChild(field);
  } else if (type == 1) {
    if (misc == 0x8000)
      field->LinkEndChild(new TiXmlText("Rope"));
    else
      field->LinkEndChild(new TiXmlText("Ladder"));
    entry->LinkEndChild(field);
  } else if (type == 2) {
    field->LinkEndChild(new TiXmlText("Door"));
    entry->LinkEndChild(field);

    field = new TiXmlElement("WarpX");
    itoa_prefix(destX, _buf, 10);
    field->LinkEndChild(new TiXmlText(_buf));
    entry->LinkEndChild(field);

    field = new TiXmlElement("WarpY");
    itoa_prefix(destY, _buf, 10);
    field->LinkEndChild(new TiXmlText(_buf));
    entry->LinkEndChild(field);

    field = new TiXmlElement("WarpDirection");
    itoa_prefix(direction, _buf, 10);
    field->LinkEndChild(new TiXmlText(_buf));
    entry->LinkEndChild(field);

    field = new TiXmlElement("WarpStyle");
    itoa_prefix(style, _buf, 10);
    field->LinkEndChild(new TiXmlText(_buf));
    entry->LinkEndChild(field);

    field = new TiXmlElement("Flag");
    itoa_prefix(flag, _buf, 16);
    field->LinkEndChild(new TiXmlText(_buf));
    entry->LinkEndChild(field);

    field = new TiXmlElement("Pointer");
    _buf[0] = '$';
    itoa(pointer, &(_buf[1]), 16);
    field->LinkEndChild(new TiXmlText(_buf));
    entry->LinkEndChild(field);
  } else if ((type == 3) || (type == 4)) {
    if (type == 3)
      field->LinkEndChild(new TiXmlText("Escalator"));
    else
      field->LinkEndChild(new TiXmlText("Stairway"));
    entry->LinkEndChild(field);

    field = new TiXmlElement("StairsDirection");
    itoa_prefix(getStairsDirection(), _buf, 10);
    field->LinkEndChild(new TiXmlText(_buf));
    entry->LinkEndChild(field);
  } else if ((type == 5) || (type == 6)) {
    if (type == 3)
      field->LinkEndChild(new TiXmlText("Object"));
    else
      field->LinkEndChild(new TiXmlText("Person"));
    entry->LinkEndChild(field);

    field = new TiXmlElement("Pointer");
    _buf[0] = '$';
    itoa(pointer, &(_buf[1]), 16);
    field->LinkEndChild(new TiXmlText(_buf));
    entry->LinkEndChild(field);
  } else {
    field->LinkEndChild(new TiXmlText("???"));
    entry->LinkEndChild(field);
  }

  return entry;
}

int Door::getStairsDirection() {
  if ((misc & 0xffff) == 0x8000)
    return 4;
  else
    return misc / 0x100;
}

void Door::setStairsDirection(int dir) {
  if (dir == 4)
    misc = 0x8000;
  else
    misc = dir * 0x100;
}

DoorModule::~DoorModule() {
  list<Door*>::iterator it;
  int i;
  for (i=0; i<NUM_AREAS; i++) {
    for (it = doors[i].begin(); it != doors[i].end(); it++) {
      delete (*it);
    }
    doors[i].clear();
  }
}

const char* DoorModule::getName() {
  return "Doors";
}

const char* DoorModule::getCredits() {
  return "Written by MrTenda";
}

void DoorModule::readFromRom(Rom &rom) {
  int i, j, len;
  uint x, y, type, ptr;
  Door *d;
  for (i=0; i<NUM_AREAS; i++) {
    rom.seek(toRegPointer(rom.readMulti(DOOR_PTR_TABLE + i*4, 4)));
    len = rom.readMulti(2);
    for (j=0; j<len; j++) {
      y = rom.read();
      x = rom.read();
      type = rom.read();
      ptr = rom.readMulti(2);
      d = new Door(x, y, type, ptr, rom);
      doors[i].push_back(d);
    }
  }
}

void DoorModule::writeToRom(Rom& rom) {
  // TODO
}

void DoorModule::readFromDat(char* dirName, map<string,string> &fns, map<string, int> &labels) {
  getDatFname(fns, "doors", "doors.xml", dirName, _buf);
  TiXmlDocument doc(_buf);
  if (!doc.LoadFile()) {
    cerr << "Error: (" << getName() << ") Failed to load " << _buf << endl;
    return;
  }

  TiXmlHandle hDoc(&doc);
  TiXmlHandle hRoot(0), hSection(0);
  TiXmlElement *section, *entry;
  Door* d;

  int areaNum;

  hRoot = TiXmlHandle(hDoc.FirstChildElement().Element());

  for (section = hRoot.FirstChild("DoorArea").Element(); section; section = section->NextSiblingElement()) {
    areaNum = atoi_prefix(section->Attribute("y")) * 32 + atoi_prefix(section->Attribute("x"));

    hSection = TiXmlHandle(section);
    for (entry = hSection.FirstChild("Door").Element(); entry; entry = entry->NextSiblingElement()) {
      d = new Door(entry, labels);
      doors[areaNum].push_back(d);
    }
  }
}

void DoorModule::writeToDat(char* dirName, map<string, string> &fns) {
  TiXmlDocument doc;
  TiXmlDeclaration* decl = new TiXmlDeclaration( "1.0", "", "" );
  doc.LinkEndChild(decl);

  TiXmlElement* root = new TiXmlElement("Doors");
  doc.LinkEndChild(root);

  TiXmlElement *section, *entry;
  list<Door*>::iterator it;
  int i, x=0, y=0;

  for (i=0; i<NUM_AREAS; i++) {
    section = new TiXmlElement("DoorArea");
    section->SetAttribute("x", x);
    section->SetAttribute("y", y);
    x++;
    if (x >= 32) {
      x = 0;
      y++;
    }
    root->LinkEndChild(section);
    for (it = doors[i].begin(); it != doors[i].end(); it++) {
      entry = (*it)->createXmlEntry();
      section->LinkEndChild(entry);
    }
  }

  getDatFname(fns, "doors", "doors.xml", dirName, _buf);
  doc.SaveFile(_buf);
}
